package com.analysis.core;

import com.analysis.model.ClassDifference;
import com.analysis.model.MethodDifference;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import org.springframework.stereotype.Component;

import java.util.*;

@Slf4j
@Component
public class ClassAnalyzer {
    private static final int ASM_VERSION = Opcodes.ASM9;

    public ClassDifference analyzeClass(byte[] oldClassBytes, byte[] newClassBytes) {
        ClassReader oldReader = new ClassReader(oldClassBytes);
        ClassReader newReader = new ClassReader(newClassBytes);

        ClassNode oldNode = new ClassNode(ASM_VERSION);
        ClassNode newNode = new ClassNode(ASM_VERSION);

        oldReader.accept(oldNode, 0);
        newReader.accept(newNode, 0);

        ClassDifference diff = new ClassDifference();
        diff.setClassName(oldNode.name.replace('/', '.'));

        // 比较字段
        compareFields(oldNode, newNode, diff);
        
        // 比较方法
        compareMethods(oldNode, newNode, diff);

        if (!diff.getAddedFields().isEmpty() || !diff.getRemovedFields().isEmpty() ||
            !diff.getModifiedFields().isEmpty() || !diff.getMethodDifferences().isEmpty()) {
            diff.setType(ClassDifference.DifferenceType.MODIFIED);
        }

        return diff;
    }

    private void compareFields(ClassNode oldNode, ClassNode newNode, ClassDifference diff) {
        Map<String, FieldNode> oldFields = new HashMap<>();
        Map<String, FieldNode> newFields = new HashMap<>();

        for (FieldNode field : oldNode.fields) {
            oldFields.put(field.name + ":" + field.desc, field);
        }
        for (FieldNode field : newNode.fields) {
            newFields.put(field.name + ":" + field.desc, field);
        }

        // 检查删除的字段
        for (String fieldKey : oldFields.keySet()) {
            if (!newFields.containsKey(fieldKey)) {
                FieldNode field = oldFields.get(fieldKey);
                log.info("字段被删除: {}.{}", diff.getClassName(), field.name);
                diff.addRemovedField(field.name);
            }
        }

        // 检查新增的字段
        for (String fieldKey : newFields.keySet()) {
            if (!oldFields.containsKey(fieldKey)) {
                FieldNode field = newFields.get(fieldKey);
                log.info("新增字段: {}.{}", diff.getClassName(), field.name);
                diff.addAddedField(field.name);
            }
        }

        // 检查修改的字段
        for (String fieldKey : oldFields.keySet()) {
            if (newFields.containsKey(fieldKey)) {
                FieldNode oldField = oldFields.get(fieldKey);
                FieldNode newField = newFields.get(fieldKey);
                compareField(oldField, newField, diff);
            }
        }
    }

    private void compareMethods(ClassNode oldNode, ClassNode newNode, ClassDifference diff) {
        Map<String, MethodNode> oldMethods = new HashMap<>();
        Map<String, MethodNode> newMethods = new HashMap<>();

        for (MethodNode method : oldNode.methods) {
            oldMethods.put(method.name + method.desc, method);
        }
        for (MethodNode method : newNode.methods) {
            newMethods.put(method.name + method.desc, method);
        }

        // 检查删除的方法
        for (String methodKey : oldMethods.keySet()) {
            if (!newMethods.containsKey(methodKey)) {
                MethodNode method = oldMethods.get(methodKey);
                log.info("方法被删除: {}.{}{}", diff.getClassName(), method.name, method.desc);
                MethodDifference methodDiff = createMethodDifference(method, "方法被删除");
                methodDiff.setType(MethodDifference.DifferenceType.DELETED);
                diff.getMethodDifferences().add(methodDiff);
            }
        }

        // 检查新增的方法
        for (String methodKey : newMethods.keySet()) {
            if (!oldMethods.containsKey(methodKey)) {
                MethodNode method = newMethods.get(methodKey);
                log.info("新增方法: {}.{}{}", diff.getClassName(), method.name, method.desc);
                MethodDifference methodDiff = createMethodDifference(method, "新增方法");
                methodDiff.setType(MethodDifference.DifferenceType.ADDED);
                diff.getMethodDifferences().add(methodDiff);
            }
        }

        // 检查修改的方法
        for (String methodKey : oldMethods.keySet()) {
            if (newMethods.containsKey(methodKey)) {
                MethodNode oldMethod = oldMethods.get(methodKey);
                MethodNode newMethod = newMethods.get(methodKey);
                compareMethod(oldMethod, newMethod, diff);
            }
        }
    }

    private void compareMethod(MethodNode oldMethod, MethodNode newMethod, ClassDifference diff) {
        // 比较方法体
        boolean methodChanged = false;
        StringBuilder changes = new StringBuilder();

        // 比较方法访问标志
        if (oldMethod.access != newMethod.access) {
            changes.append("方法访问修饰符发生变化; ");
            methodChanged = true;
        }

        // 比较方法体大小
        if (oldMethod.instructions.size() != newMethod.instructions.size()) {
            changes.append("方法体大小发生变化; ");
            methodChanged = true;
        } else {
            // 比较每条指令
            Iterator<AbstractInsnNode> oldIter = oldMethod.instructions.iterator();
            Iterator<AbstractInsnNode> newIter = newMethod.instructions.iterator();
            
            int index = 0;
            while (oldIter.hasNext() && newIter.hasNext()) {
                AbstractInsnNode oldInsn = oldIter.next();
                AbstractInsnNode newInsn = newIter.next();
                
                if (!instructionsEqual(oldInsn, newInsn)) {
                    changes.append(String.format("第%d条指令发生变化; ", index));
                    methodChanged = true;
                    
                    // 如果是字符串常量的变化，记录具体的变化
                    if (oldInsn instanceof LdcInsnNode && newInsn instanceof LdcInsnNode) {
                        LdcInsnNode oldLdc = (LdcInsnNode) oldInsn;
                        LdcInsnNode newLdc = (LdcInsnNode) newInsn;
                        if (oldLdc.cst instanceof String && newLdc.cst instanceof String) {
                            changes.append(String.format("字符串常量从'%s'变更为'%s'; ", oldLdc.cst, newLdc.cst));
                        }
                    }
                }
                index++;
            }
        }

        // 比较异常表
        if (!Objects.equals(oldMethod.exceptions, newMethod.exceptions)) {
            changes.append("方法异常处理发生变化; ");
            methodChanged = true;
        }

        // 如果方法有变化，创建差异记录
        if (methodChanged) {
            log.info("方法被修改: {}.{}{} - {}", diff.getClassName(), oldMethod.name, oldMethod.desc, changes);
            MethodDifference methodDiff = createMethodDifference(oldMethod, changes.toString());
            methodDiff.setType(MethodDifference.DifferenceType.MODIFIED);
            diff.getMethodDifferences().add(methodDiff);
        }
    }

    private boolean instructionsEqual(AbstractInsnNode insn1, AbstractInsnNode insn2) {
        if (insn1.getClass() != insn2.getClass()) return false;
        if (insn1.getOpcode() != insn2.getOpcode()) return false;

        // 对不同类型的指令进行特殊处理
        if (insn1 instanceof LdcInsnNode) {
            LdcInsnNode ldc1 = (LdcInsnNode) insn1;
            LdcInsnNode ldc2 = (LdcInsnNode) insn2;
            return Objects.equals(ldc1.cst, ldc2.cst);
        } else if (insn1 instanceof VarInsnNode) {
            VarInsnNode var1 = (VarInsnNode) insn1;
            VarInsnNode var2 = (VarInsnNode) insn2;
            return var1.var == var2.var;
        } else if (insn1 instanceof MethodInsnNode) {
            MethodInsnNode method1 = (MethodInsnNode) insn1;
            MethodInsnNode method2 = (MethodInsnNode) insn2;
            return method1.owner.equals(method2.owner) &&
                   method1.name.equals(method2.name) &&
                   method1.desc.equals(method2.desc);
        }
        // 可以添加更多类型的指令比较

        return true;
    }

    private void compareField(FieldNode oldField, FieldNode newField, ClassDifference diff) {
        // 检查访问修饰符变化
        if (oldField.access != newField.access) {
            String modification = String.format("访问修饰符从 %s 变更为 %s",
                    getAccessString(oldField.access),
                    getAccessString(newField.access));
            log.info("字段修改: {}.{} - {}", diff.getClassName(), oldField.name, modification);
            diff.addModifiedField(oldField.name, modification);
        }
        
        // 检查字段值变化
        if (oldField.value != null || newField.value != null) {
            if (oldField.value == null && newField.value != null) {
                String modification = String.format("初始值从 null 变更为 %s", formatValue(newField.value));
                log.info("字段修改: {}.{} - {}", diff.getClassName(), oldField.name, modification);
                diff.addModifiedField(oldField.name, modification);
            } else if (oldField.value != null && newField.value == null) {
                String modification = String.format("初始值从 %s 变更为 null", formatValue(oldField.value));
                log.info("字段修改: {}.{} - {}", diff.getClassName(), oldField.name, modification);
                diff.addModifiedField(oldField.name, modification);
            } else if (oldField.value != null && !oldField.value.equals(newField.value)) {
                String modification = String.format("初始值从 %s 变更为 %s",
                        formatValue(oldField.value),
                        formatValue(newField.value));
                log.info("字段修改: {}.{} - {}", diff.getClassName(), oldField.name, modification);
                diff.addModifiedField(oldField.name, modification);
            }
        }
    }

    private MethodDifference createMethodDifference(MethodNode method, String description) {
        MethodDifference diff = new MethodDifference();
        diff.setMethodName(method.name);
        diff.setSignature(method.desc);
        diff.setDescription(description);
        return diff;
    }

    private String getAccessString(int access) {
        List<String> modifiers = new ArrayList<>();
        if ((access & Opcodes.ACC_PUBLIC) != 0) modifiers.add("public");
        if ((access & Opcodes.ACC_PRIVATE) != 0) modifiers.add("private");
        if ((access & Opcodes.ACC_PROTECTED) != 0) modifiers.add("protected");
        if ((access & Opcodes.ACC_STATIC) != 0) modifiers.add("static");
        if ((access & Opcodes.ACC_FINAL) != 0) modifiers.add("final");
        if ((access & Opcodes.ACC_VOLATILE) != 0) modifiers.add("volatile");
        if ((access & Opcodes.ACC_TRANSIENT) != 0) modifiers.add("transient");
        return String.join(" ", modifiers);
    }

    private String formatValue(Object value) {
        if (value == null) return "null";
        if (value instanceof String) {
            return "\"" + value + "\"";
        }
        return value.toString();
    }
}
